package core;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * 数字工具类
 * <p>
 * 对于精确值计算应该使用 {@link BigDecimal}
 * </p>
 */
public class NumberUtil {

    /**
     * 默认除法运算精度
     */
    private static final int DEFAULT_DIV_SCALE = 10;

    // region ----- isNumber

    /**
     * 判断字符串是否为数字
     *
     * @param str 字符串值
     * @return 是否为数字
     */
    public static boolean isNumber(String str) {
        if (ObjectUtil.isEmpty(str)) {
            return false;
        }
        char[] chars = str.toCharArray();
        int pointNum = 0;
        int startIndex = 0;
        if (chars[0] == '-' || chars[0] == '+') {
            startIndex = 1;
        }
        for (int i = startIndex; i < chars.length; i++) {
            if (chars[i] == '.') {
                pointNum++;
                if (pointNum > 1) {
                    return false;
                }
            } else if (!Character.isDigit(chars[i])) {
                return false;
            }
        }
        return true;
    }

    // endregion

    // region ----- conversion

    /**
     * 转换为BigDecimal
     * <p>
     * 如果传入的值为空，则返回0
     * </p>
     *
     * @param number 数字
     * @return BigDecimal
     */
    public static BigDecimal toBigDecimal(Number number) {
        if (null == number) {
            return BigDecimal.ZERO;
        }
        return toBigDecimal(number.toString());
    }

    /**
     * 转换为BigDecimal
     * <p>
     * 如果传入的值为空，则返回0
     * </p>
     *
     * @param numberStr 数字字符串
     * @return BigDecimal
     */
    public static BigDecimal toBigDecimal(String numberStr) {
        if (ObjectUtil.isEmpty(numberStr)) {
            return BigDecimal.ZERO;
        }
        try {
            return new BigDecimal(numberStr);
        } catch (NumberFormatException e) {
            return BigDecimal.ZERO;
        }
    }

    // endregion

    // region ----- calculation

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(float v1, float v2) {
        return add(Float.toString(v1), Float.toString(v2));
    }

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(double v1, double v2) {
        return add(Double.toString(v1), Double.toString(v2));
    }

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(String v1, String v2) {
        return toBigDecimal(v1).add(toBigDecimal(v2));
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(float v1, float v2) {
        return sub(Float.toString(v1), Float.toString(v2));
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(double v1, double v2) {
        return sub(Double.toString(v1), Double.toString(v2));
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(String v1, String v2) {
        return toBigDecimal(v1).subtract(toBigDecimal(v2));
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(float v1, float v2) {
        return mul(Float.toString(v1), Float.toString(v2));
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(double v1, double v2) {
        return mul(Double.toString(v1), Double.toString(v2));
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(String v1, String v2) {
        return toBigDecimal(v1).multiply(toBigDecimal(v2));
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到小数点以后10位，后面的四舍五入
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal div(float v1, float v2) {
        return div(v1, v2, DEFAULT_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到小数点以后10位，后面的四舍五入
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal div(double v1, double v2) {
        return div(v1, v2, DEFAULT_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到小数点以后10位，后面的四舍五入
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal div(String v1, String v2) {
        return div(v1, v2, DEFAULT_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，由scale参数指定精度，后面的四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static BigDecimal div(float v1, float v2, int scale) {
        return div(Float.toString(v1), Float.toString(v2), scale);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，由scale参数指定精度，后面的四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static BigDecimal div(double v1, double v2, int scale) {
        return div(Double.toString(v1), Double.toString(v2), scale);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，由scale参数指定精度，后面的四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static BigDecimal div(String v1, String v2, int scale) {
        return div(v1, v2, scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，由scale参数指定精度
     *
     * @param v1           被除数
     * @param v2           除数
     * @param scale        表示表示需要精确到小数点以后几位
     * @param roundingMode 保留小数的模式 {@link RoundingMode}
     * @return 两个参数的商
     */
    public static BigDecimal div(String v1, String v2, int scale, RoundingMode roundingMode) {
        return toBigDecimal(v1).divide(toBigDecimal(v2), scale, roundingMode);
    }

    // endregion

    // region ----- compare

    /**
     * 比较两个值的大小
     *
     * @param v1 值1
     * @param v2 值2
     * @return 如果v1大于v2返回1，等于返回0，小于返回-1
     */
    public static int compare(BigDecimal v1, BigDecimal v2) {
        if (null == v1 || null == v2) {
            throw new IllegalArgumentException("比较值不能为空");
        }
        return v1.compareTo(v2);
    }

    /**
     * 比较两个值的大小
     *
     * @param v1 值1
     * @param v2 值2
     * @return 如果v1大于v2返回true，否则返回false
     */
    public static boolean isGreater(BigDecimal v1, BigDecimal v2) {
        return compare(v1, v2) > 0;
    }

    /**
     * 比较两个值的大小
     *
     * @param v1 值1
     * @param v2 值2
     * @return 如果v1小于v2返回true，否则返回false
     */
    public static boolean isLess(BigDecimal v1, BigDecimal v2) {
        return compare(v1, v2) < 0;
    }

    /**
     * 比较两个值的大小
     *
     * @param v1 值1
     * @param v2 值2
     * @return 如果v1等于v2返回true，否则返回false
     */
    public static boolean isEqual(BigDecimal v1, BigDecimal v2) {
        return compare(v1, v2) == 0;
    }

    // endregion

    // region ----- format

    /**
     * 格式化数字
     *
     * @param number  数字值
     * @param pattern 格式
     * @return 格式化后的字符串
     */
    public static String format(Number number, String pattern) {
        if (null == number) {
            return null;
        }
        if (ObjectUtil.isEmpty(pattern)) {
            return number.toString();
        }
        final NumberFormat format = new DecimalFormat(pattern);
        return format.format(number);
    }

    // endregion

    // region ----- range

    /**
     * 判断值是否在指定范围内
     *
     * @param value 值
     * @param min   最小值（包含）
     * @param max   最大值（包含）
     * @return 是否在范围内
     */
    public static boolean isBetween(BigDecimal value, BigDecimal min, BigDecimal max) {
        if (null == value || null == min || null == max) {
            return false;
        }
        return value.compareTo(min) >= 0 && value.compareTo(max) <= 0;
    }

    // endregion
}